//! Step execution engine tests
//! 
//! Tests for the step execution functionality

use ai_test_platform::{
    actors::{
        step::{StepActor, ValidationResult},
    },
    models::{
        config::StepConfig,
        step::{StepContext, StepResult, StepType},
        Value, ValueMap,
    },
};
use serde_json::json;
use uuid::Uuid;

#[tokio::test]
async fn test_step_actor_creation() {
    let step_id = Uuid::new_v4();
    let step_actor = StepActor::new(step_id, None, None);
    
    // Should be able to create step actor
    assert_eq!(format!("{:?}", step_actor).contains(&step_id.to_string()), true);
}

#[tokio::test]
async fn test_validation_result_structure() {
    let validation_result = ValidationResult {
        success: true,
        errors: Vec::new(),
    };
    
    assert!(validation_result.success);
    assert!(validation_result.errors.is_empty());
    
    let validation_result_with_errors = ValidationResult {
        success: false,
        errors: vec!["Error 1".to_string(), "Error 2".to_string()],
    };
    
    assert!(!validation_result_with_errors.success);
    assert_eq!(validation_result_with_errors.errors.len(), 2);
}

#[tokio::test]
async fn test_step_config_creation() {
    let step_config = StepConfig::new("test_step".to_string());

    assert_eq!(step_config.step_name, "test_step");
    assert!(step_config.parameters.is_empty());
    assert!(step_config.validators.is_empty());
}

#[tokio::test]
async fn test_step_config_with_params() {
    let mut step_config = StepConfig::new("api_request".to_string());

    // Add HTTP request parameters
    step_config.parameters.insert("method".to_string(), Value::from("GET"));
    step_config.parameters.insert("url".to_string(), Value::from("https://httpbin.org/get"));

    let mut headers = serde_json::Map::new();
    headers.insert("Content-Type".to_string(), Value::from("application/json"));
    step_config.parameters.insert("headers".to_string(), Value::Object(headers));

    assert_eq!(step_config.parameters.len(), 3);
    assert_eq!(step_config.parameters.get("method"), Some(&Value::from("GET")));
    assert_eq!(step_config.parameters.get("url"), Some(&Value::from("https://httpbin.org/get")));
}

#[tokio::test]
async fn test_step_config_with_validators() {
    let mut step_config = StepConfig::new("api_request".to_string());

    // Add validators
    let status_validator = json!({
        "type": "status_code",
        "expected": 200
    });

    let json_path_validator = json!({
        "type": "json_path",
        "path": "$.success",
        "expected": true
    });

    step_config.validators.push(status_validator);
    step_config.validators.push(json_path_validator);

    assert_eq!(step_config.validators.len(), 2);
}

#[tokio::test]
async fn test_step_context_creation() {
    let test_case_id = Uuid::new_v4();
    let session_id = Uuid::new_v4();
    let mut context = StepContext::new(test_case_id, session_id);

    // Add some variables
    context.variables.insert("api_key".to_string(), Value::from("test-key-123"));
    context.variables.insert("base_url".to_string(), Value::from("https://api.example.com"));

    assert_eq!(context.variables.len(), 2);
    assert_eq!(context.variables.get("api_key"), Some(&Value::from("test-key-123")));
}

#[tokio::test]
async fn test_step_result_creation() {
    let result = StepResult::new(
        Uuid::new_v4(),
        "test_step".to_string(),
        StepType::API,
    ).success();

    assert_eq!(result.name, "test_step");
    assert_eq!(result.step_type, StepType::API);
    assert!(result.success);
}

#[tokio::test]
async fn test_step_result_with_response() {
    let mut result = StepResult::new(
        Uuid::new_v4(),
        "api_request".to_string(),
        StepType::API,
    ).success();

    let response = json!({
        "status_code": 200,
        "json": {
            "message": "success",
            "data": [1, 2, 3]
        }
    });

    // For now, just test that the result was created successfully
    // TODO: Add response storage when StepResult supports it
    assert_eq!(result.name, "api_request");
    assert_eq!(result.step_type, StepType::API);
    assert!(result.success);
}

#[tokio::test]
async fn test_step_types_enum() {
    let step_types = vec![
        StepType::API,
        StepType::Request,
        StepType::TestCase,
        StepType::Transaction,
        StepType::WebSocket,
    ];

    for step_type in step_types {
        let step_result = StepResult::new(Uuid::new_v4(), "test".to_string(), step_type.clone());
        assert_eq!(step_result.step_type, step_type);

        // Test serialization
        let json_value = serde_json::to_value(&step_result).unwrap();
        assert!(json_value["step_type"].is_string());
    }
}

#[tokio::test]
async fn test_step_config_serialization() {
    let mut step_config = StepConfig::new("complex_step".to_string());

    // Add complex parameters
    step_config.parameters.insert("method".to_string(), Value::from("POST"));
    step_config.parameters.insert("url".to_string(), Value::from("https://api.example.com/users"));
    step_config.parameters.insert("json".to_string(), json!({
        "name": "John Doe",
        "email": "john@example.com"
    }));

    // Add validators
    step_config.validators.push(json!({
        "type": "status_code",
        "expected": 201
    }));

    // Test serialization
    let json_value = serde_json::to_value(&step_config).unwrap();
    assert!(json_value.is_object());
    assert_eq!(json_value["step_name"], "complex_step");
    assert!(json_value["parameters"].is_object());
    assert!(json_value["validators"].is_array());

    // Test deserialization
    let deserialized: StepConfig = serde_json::from_value(json_value).unwrap();
    assert_eq!(deserialized.step_name, step_config.step_name);
    assert_eq!(deserialized.parameters.len(), step_config.parameters.len());
    assert_eq!(deserialized.validators.len(), step_config.validators.len());
}

#[tokio::test]
async fn test_step_context_variable_substitution() {
    let test_case_id = Uuid::new_v4();
    let session_id = Uuid::new_v4();
    let mut context = StepContext::new(test_case_id, session_id);
    context.variables.insert("user_id".to_string(), Value::from("12345"));
    context.variables.insert("api_version".to_string(), Value::from("v1"));

    // Test that variables can be accessed
    assert_eq!(context.variables.get("user_id"), Some(&Value::from("12345")));
    assert_eq!(context.variables.get("api_version"), Some(&Value::from("v1")));
    assert_eq!(context.variables.get("non_existent"), None);
}

#[tokio::test]
async fn test_step_result_variables() {
    let mut result = StepResult::new(
        Uuid::new_v4(),
        "metadata_test".to_string(),
        StepType::API,
    ).success();

    // Test basic result properties
    assert_eq!(result.name, "metadata_test");
    assert_eq!(result.step_type, StepType::API);
    assert!(result.success);
}

#[tokio::test]
async fn test_step_config_builder_pattern() {
    // Test building step config in a fluent style
    let mut step_config = StepConfig::new("builder_test".to_string());

    // Add parameters
    step_config.parameters.insert("method".to_string(), Value::from("GET"));
    step_config.parameters.insert("url".to_string(), Value::from("https://httpbin.org/get"));

    // Add timeout
    step_config.parameters.insert("timeout".to_string(), Value::from(30.0));

    // Add validators
    step_config.validators.push(json!({
        "type": "status_code",
        "expected": 200
    }));

    step_config.validators.push(json!({
        "type": "response_time",
        "max_ms": 5000
    }));

    // Verify all properties are set
    assert_eq!(step_config.step_name, "builder_test");
    assert_eq!(step_config.parameters.len(), 3);
    assert_eq!(step_config.validators.len(), 2);
}

#[tokio::test]
async fn test_step_execution_error_handling() {
    // Test step config with missing required parameters
    let step_config = StepConfig::new("invalid_step".to_string());
    // Missing URL parameter should cause execution to fail

    assert_eq!(step_config.parameters.get("url"), None);

    // Test step config with invalid field handling
    let json_str = r#"{
        "step_name": "test_step",
        "variables": {},
        "parameters": {},
        "validators": []
    }"#;

    let result: Result<StepConfig, _> = serde_json::from_str(json_str);
    assert!(result.is_ok()); // Should succeed with valid structure
}
